﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Reflection;
using Microsoft.Practices.EnterpriseLibrary.Logging;

namespace Logging
{
    /// <summary>
    /// MtpLogger is a utility class for logging in MTP. The class contain several methods for creating log entries.
    /// </summary>
    public static class MyLogger
    {
        /// <summary>
        /// Gets or sets whether the logger is running in testmode (this means that calling logging methods will do nothing).
        /// </summary>
        public static bool TestMode { get; set; }

        /// <summary>
        /// Creates a info log entry with specified message.
        /// </summary>
        /// <param name="message">The message of the log entry.</param>
        public static void LogInfo(string message)
        {
            if (TestMode)
            {
                return;
            }
            LogEntry entry = new LogEntry();
            entry.EventId = LoggingConstants.EventIdInfo;
            entry.Priority = LoggingConstants.PriorityInfo;
            entry.Categories.Add(LoggingConstants.CategoryInfo);
            entry.MachineName = Dns.GetHostName();
            entry.Message = message;
            Logger.Write(entry);
        }

        /// <summary>
        /// Creates a log trace entry with specified message.
        /// </summary>
        /// <param name="message">The message of the log entry.</param>
        public static void LogTrace(string message)
        {
            if (TestMode)
            {
                return;
            }
            LogEntry entry = new LogEntry();
            entry.EventId = LoggingConstants.EventIdTrace;
            entry.Priority = LoggingConstants.PriorityTrace;
            entry.Categories.Add(LoggingConstants.CategoryTrace);
            entry.MachineName = Dns.GetHostName();
            entry.Message = message;
            Logger.Write(entry);
        }

        /// <summary>
        /// Creates a log trace entry with specified message for a method entry.
        /// </summary>
        /// <param name="method">The method that is entered.</param>
        public static void LogTraceEntry(string method)
        {
            if (TestMode)
            {
                return;
            }
            LogEntry entry = new LogEntry();
            entry.EventId = LoggingConstants.EventIdTrace;
            entry.Priority = LoggingConstants.PriorityTrace;
            entry.Categories.Add(LoggingConstants.CategoryTrace);
            entry.MachineName = Dns.GetHostName();
            entry.Message = "ENTRY: " + method;
            Logger.Write(entry);
        }

        /// <summary>
        /// Creates a log trace entry with specified message for a method entry with specifed request object.
        /// </summary>
        /// <param name="method">The method that is entered.</param>
        /// <param name="value">The request object used when calling the method.</param>
        public static void LogTraceEntry(string method, object value)
        {
            if (TestMode)
            {
                return;
            }
            StringBuilder sb = new StringBuilder("ENTRY: ");
            sb.Append(method);
            sb.Append(Environment.NewLine);
            sb.Append("ObjectData: ");
            sb.Append(Environment.NewLine);
            sb.Append(GetObjectData(value));
            LogEntry entry = new LogEntry();
            entry.EventId = LoggingConstants.EventIdTrace;
            entry.Priority = LoggingConstants.PriorityTrace;
            entry.Categories.Add(LoggingConstants.CategoryTrace);
            entry.MachineName = Dns.GetHostName();
            entry.Message = sb.ToString();
            Logger.Write(entry);
        }

        /// <summary>
        /// Creates a log trace entry with specified message for a method entry with specifed request object.
        /// </summary>
        /// <param name="method">The method that is entered.</param>
        /// <param name="values">The request object parameters used when calling the method.</param>
        public static void LogTraceEntry(string method, params object[] values)
        {
            if (TestMode)
            {
                return;
            }
            StringBuilder sb = new StringBuilder("ENTRY: ");
            sb.Append(method);
            sb.Append(Environment.NewLine);
            sb.Append("ObjectData: ");
            sb.Append(Environment.NewLine);
            for (int i = 0; i < values.Length; i++)
            {
                sb.Append(GetObjectData(values[i]));
            }
            LogEntry entry = new LogEntry();
            entry.EventId = LoggingConstants.EventIdTrace;
            entry.Priority = LoggingConstants.PriorityTrace;
            entry.Categories.Add(LoggingConstants.CategoryTrace);
            entry.MachineName = Dns.GetHostName();
            entry.Message = sb.ToString();
            Logger.Write(entry);
        }

        /// <summary>
        /// Creates a log trace entry with specified message for a method exit.
        /// </summary>
        /// <param name="method">The method that is exited.</param>
        public static void LogTraceExit(string method)
        {
            if (TestMode)
            {
                return;
            }
            LogEntry entry = new LogEntry();
            entry.EventId = LoggingConstants.EventIdTrace;
            entry.Priority = LoggingConstants.PriorityTrace;
            entry.Categories.Add(LoggingConstants.CategoryTrace);
            entry.MachineName = Dns.GetHostName();
            entry.Message = "EXIT: " + method;
            Logger.Write(entry);
        }

        /// <summary>
        /// Creates a log exception entry with specified message and exception to be logged.
        /// </summary>
        /// <param name="message">The message of the log entry.</param>
        /// <param name="exception">The exception that caused logging to occur.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        public static void LogException(string message, Exception exception)
        {
            if (TestMode)
            {
                return;
            }
            LogEntry entry = new LogEntry();
            entry.EventId = LoggingConstants.EventIdException;
            entry.Priority = LoggingConstants.PriorityException;
            entry.Categories.Add(LoggingConstants.CategoryException);
            entry.MachineName = Dns.GetHostName();
            string exceptionDetails = "";
            try
            {
                if (exception.InnerException != null)
                {
                    exceptionDetails = GetObjectData(exception.InnerException);
                }
            }
            catch
            { }
            entry.Message = message + Environment.NewLine + exception.Message + Environment.NewLine + exception.ToString() + exceptionDetails;
            Logger.Write(entry);
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.Int32.ToString")]
        private static string GetObjectData(object obj)
        {
            StringBuilder stringBuilder = new StringBuilder();

            if (obj == null)
            {
                AppendStringToLog(stringBuilder, "null", false);
            }
            else
            {
                Type type = obj.GetType();
                if (type.BaseType == typeof(Array))
                {
                    int index = 0;
                    Array arrayList = (Array)obj;
                    foreach (object arrayItem in arrayList)
                    {
                        string temp = "[" + index.ToString() + "]";
                        AppendStringToLog(stringBuilder, type.FullName.Replace("[]", temp) + ": ", true);
                        AppendStringToLog(stringBuilder, GetObjectData(arrayItem), true);
                        index++;
                    }
                }
                else
                {
                    if (!IsNative(obj.GetType()))
                    {
                        PropertyInfo[] propertyInfos = type.GetProperties();
                        foreach (PropertyInfo propertyInfo in propertyInfos)
                        {
                            if (propertyInfo.CanRead)
                            {
                                if (IsNative(propertyInfo.PropertyType))
                                {
                                    object value = propertyInfo.GetValue(obj, null);
                                    string valueString = value != null ? value.ToString() : "";
                                    AppendStringToLog(stringBuilder, type.FullName + "." + propertyInfo.Name + ": " + valueString, true);
                                }
                                else
                                {
                                    AppendStringToLog(stringBuilder, type.FullName + "." + propertyInfo.Name + ": ", true);
                                    AppendStringToLog(stringBuilder, GetObjectData(propertyInfo.GetValue(obj, null)), true);
                                }
                            }
                        }
                    }
                    else
                    {
                        AppendStringToLog(stringBuilder, type.FullName + ": " + obj.ToString(), false);
                    }
                }
            }

            return stringBuilder.ToString();
        }

        private static bool IsNative(Type type)
        {
            if (type == typeof(int)
                || type == typeof(string)
                || type == typeof(DateTime)
                || type == typeof(double)
                || type == typeof(decimal)
                || type == typeof(float)
                || type == typeof(byte)
                || type == typeof(bool)
                || type == typeof(char)
                || type == typeof(long)
                || type == typeof(short)
                || type == typeof(sbyte)
                || type == typeof(Guid)
                || type == typeof(uint)
                || type == typeof(ushort)
                || type == typeof(ulong)
                || type.BaseType == typeof(Enum))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1820:TestForEmptyStringsUsingStringLength")]
        private static void AppendStringToLog(StringBuilder stringBuilder, string value, bool newline)
        {
            if (!string.IsNullOrEmpty(value) && value.Trim() != string.Empty)
            {
                if (newline && stringBuilder.ToString().Trim() != string.Empty)
                {
                    stringBuilder = stringBuilder.Append(Environment.NewLine + value);
                }
                else
                {
                    stringBuilder = stringBuilder.Append(value);
                }
            }
        }

    }
}
